package com.test.learnopenglandroid;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap;
import java.util.Map;

public final class OpenGLHelper2 {

	
	private Map<String, Integer > mHandleCacheMap = new HashMap<String, Integer>();
	private int mProgram = -1;

	public OpenGLHelper2(int programId, int vshader, int fshader ){
		mProgram = programId;

		mHandleCacheMap.put( "Program", programId);
		mHandleCacheMap.put( "VertextShader", vshader);
		mHandleCacheMap.put( "FragmentShader", fshader);

	}
	public void setupTarget( int target, int[] crop, boolean clear ){
		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, target);
		GLES20.glViewport(crop[0], crop[1], crop[2], crop[3]);
		if( clear ){
			GLES20.glClearColor(0, 0.0f, 0, 1.0f);
			GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
		}
	}
	public void active(){
		GLES20.glUseProgram( mProgram );
	}
	public void release(){
		deleteProgram( new int[]{mHandleCacheMap.get("Program"),
				mHandleCacheMap.get("VertextShader"),
				mHandleCacheMap.get("FragmentShader")});
		mHandleCacheMap.clear();
	}
	public int getHandle(String name, String type){
		int handle = -1;
		if( mHandleCacheMap.containsKey(name))
			handle = mHandleCacheMap.get(name);
		else{
			if( type == "attribute")
				handle = GLES20.glGetAttribLocation(mProgram, name);
			else if( type == "uniform")
				handle = GLES20.glGetUniformLocation(mProgram, name);
			if( handle < 0 )
				throw new RuntimeException("Could not locate parameter: " + name );
			mHandleCacheMap.put(name, handle);
		}

		return handle;
	}
	public int[] genVertexGrid( int wcnt, int hcnt, float x1, float x2, float y1, float y2 ){
		float width = x2-x1, height = y2-y1;
		float[] data = new float[(wcnt+1)*(hcnt+1)*2];
		int pos = 0;
		for( int i=0; i<hcnt+1; i+=1 )
			for( int j=0; j<wcnt+1; j+=1 ){
				data[pos++] = x1 + j*width/wcnt;
				data[pos++] = y1 + i*height/hcnt;
			}
		FloatBuffer mVsBuffer = ByteBuffer.allocateDirect(data.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
		mVsBuffer.put(data).position(0);

		return new int[]{ genArrayBuffer(mVsBuffer),data.length };

	}
	public int[] genIndexGrid(int wcnt, int hcnt){
		int stride = wcnt+1, bi = 0;
		short[] indx = new short[wcnt*hcnt*6];
		for( int i=0; i<hcnt; i++ )
			for( int j=0; j<wcnt; j++ ){
			short pi = (short)(i*stride+j);
			indx[bi++] = pi; indx[bi++] = (short) (pi+stride);indx[bi++] = (short) (pi+1);
			indx[bi++] = (short) (pi+1); indx[bi++] = (short)(pi+stride);indx[bi++] = (short) (pi+1+stride);
		}
		ShortBuffer mIndexBuffer = ByteBuffer.allocateDirect(indx.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer();
		mIndexBuffer.put(indx).position(0);
		return new int[]{ genElementBuffer(mIndexBuffer),indx.length };
	}
	public int[][] genStandardGrid( int wcnt, int hcnt ){
		return new int[][]{ genVertexGrid(wcnt,hcnt,0,1,0,1),genIndexGrid(wcnt,hcnt)};
	}
	public static float[] genStandardMatrix( boolean screen ){
		float[] mVMatrix = new float[16];
		float[] mProjMatrix = new float[16];
		float[] displayMatrix = new float[16];
		float[] matrix = new float[16];
		Matrix.setLookAtM(mVMatrix, 0, 0, 0, 4, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
		if( screen )
			Matrix.orthoM(mProjMatrix, 0, 0, 1, 1, 0,3, 7);
		else
			Matrix.orthoM(mProjMatrix, 0, 0, 1, 0, 1,3, 7);
		Matrix.setIdentityM(displayMatrix,0);
    	Matrix.setIdentityM(matrix,0);
        Matrix.multiplyMM(matrix, 0, mVMatrix, 0, displayMatrix, 0);
        Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0);
		return matrix;
	}
	public static float[] genCropMatrix( boolean screen, int[]dst, int[] virdst ){
		float[] mVMatrix = new float[16];
		float[] mProjMatrix = new float[16];
		float[] displayMatrix = new float[16];
		float[] matrix = new float[16];
		Matrix.setLookAtM(mVMatrix, 0, 0, 0, 4, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
		
		float x = (float)(dst[0]-virdst[0])/(virdst[2]);
		float y = (float)(dst[1]-virdst[1])/(virdst[3]);
		float w = (float)dst[2]/virdst[2];
		float h = (float)dst[3]/virdst[3];
		
		if( screen )
			Matrix.orthoM(mProjMatrix, 0, x, x+w, y+h, y,3, 7);
		else
			Matrix.orthoM(mProjMatrix, 0, x, x+w, y, y+h,3, 7);
		Matrix.setIdentityM(displayMatrix,0);
    	Matrix.setIdentityM(matrix,0);
        Matrix.multiplyMM(matrix, 0, mVMatrix, 0, displayMatrix, 0);
        Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0);
		return matrix;
	}
	public void setTexture(String name, int texture, int id ){
		
		int[] inds = new int[]{ GLES20.GL_TEXTURE0, GLES20.GL_TEXTURE1, GLES20.GL_TEXTURE2, GLES20.GL_TEXTURE3,
								GLES20.GL_TEXTURE4, GLES20.GL_TEXTURE5, GLES20.GL_TEXTURE6, GLES20.GL_TEXTURE7};
		
        GLES20.glActiveTexture(inds[id]);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
        int handle = getHandle(name,"uniform");
		GLES20.glUniform1i( handle, id );
	}
	public void setOESTexture(String name, int texture, int id ){

		int[] inds = new int[]{ GLES20.GL_TEXTURE0, GLES20.GL_TEXTURE1, GLES20.GL_TEXTURE2, GLES20.GL_TEXTURE3,
				GLES20.GL_TEXTURE4, GLES20.GL_TEXTURE5, GLES20.GL_TEXTURE6, GLES20.GL_TEXTURE7};

		GLES20.glActiveTexture(inds[id]);
		GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
		int handle = getHandle(name,"uniform");
		GLES20.glUniform1i( handle, id );
	}

	public void setUniform(String name, int[] v ){
		int handle = getHandle(name,"uniform");
		GLES20.glUniform1iv(handle, v.length, v, 0);
	}
	public void setUniform(String name, float v ){
		int handle = getHandle(name,"uniform");
		GLES20.glUniform1f( handle, v );
	}
	
	public void setUniform(String name, int v ){
		int handle = getHandle(name,"uniform");
		GLES20.glUniform1i( handle, v );
	}
	public void setUniform(String name, float[] v ){
		int handle = getHandle(name,"uniform");
		GLES20.glUniform1fv(handle, v.length, v, 0);
	}
	public void setMatrix(String name, float[] matrix ){
		int handle = getHandle(name,"uniform");
		GLES20.glUniformMatrix4fv(handle, 1, false, matrix, 0);
	}
	
	public void setAttribute(String name, FloatBuffer buffer, int count, int stride ){
		int handle = getHandle(name,"attribute");
		buffer.position(0);
    	GLES20.glVertexAttribPointer(handle,count, GLES20.GL_FLOAT, false, stride, buffer);
 		GLES20.glEnableVertexAttribArray(handle);
	}
	public void setAttribute(String name, int vbo, int count, int stride ){
		int handle = getHandle(name,"attribute");
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo);
    	GLES20.glVertexAttribPointer(handle,count, GLES20.GL_FLOAT, false, stride, 0);
 		GLES20.glEnableVertexAttribArray(handle);
 		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
	}
	public void setAttribute(String name, int vbo, int offset, int count, int stride ){
		int handle = getHandle(name,"attribute");
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo);
		GLES20.glVertexAttribPointer(handle,count, GLES20.GL_FLOAT, false, stride, offset);
		GLES20.glEnableVertexAttribArray(handle);
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
	}
	public void disableAttribute( String name ){
		int handle = getHandle(name,"attribute");
		GLES20.glDisableVertexAttribArray(handle);
	}
	public int genArrayBuffer( FloatBuffer vertex){
		int[] vbo = new int[1];
		GLES20.glGenBuffers(1, vbo, 0 );
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo[0] );
		GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertex.capacity()*4, vertex, GLES20.GL_STATIC_DRAW );
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0 );
		return vbo[0];
	}
	public int genArrayBuffer( ByteBuffer vertex){
		int[] vbo = new int[1];
		GLES20.glGenBuffers(1, vbo, 0 );
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo[0] );
		GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertex.capacity(), vertex, GLES20.GL_STATIC_DRAW );
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0 );
		return vbo[0];
	}
	public int updateArrayBuffer( int vbo, FloatBuffer vertex){
		updateArrayBuffer( vbo, vertex, vertex.capacity() );
		return vbo;
	}
	public int updateArrayBuffer(int vbo, FloatBuffer vertex, int size){
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo );
		GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, size*4, vertex, GLES20.GL_STATIC_DRAW );
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0 );
		return vbo;
	}
	public int genElementBuffer( ShortBuffer inb){
		int[] vbo = new int[1];
		GLES20.glGenBuffers(1, vbo, 0 );
		GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, vbo[0] );
		GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, inb.capacity()*2, inb, GLES20.GL_STATIC_DRAW );
		GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0 );
		return vbo[0];
	}
	public void drawElement(ShortBuffer index){
		GLES20.glDrawElements(GLES20.GL_TRIANGLES, index.capacity() , GLES20.GL_UNSIGNED_SHORT, index);
	}
	public void drawElement(int vbo, int count){
		GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, vbo);
    	GLES20.glDrawElements(GLES20.GL_TRIANGLES, count , GLES20.GL_UNSIGNED_SHORT, 0);
    	GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0 );
	}

//	private native static int[] createProgramByName(String name );
//	private native static void deleteProgramByName( int[]handle );

//	static public OpenGLHelper2 createProgram(String name) {
//		int[] ret = createProgramByName(name);
//		if(ret!=null)
//			return new OpenGLHelper2(ret[0],ret[1],ret[2]);
//		else
//			return null;
//	}

//	static public OpenGLHelper2 createProgram(String vertexSource, String fragmentSource) {
//		int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
//        if (vertexShader == 0) {
//            return null;
//        }
//        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
//        if (pixelShader == 0) {
//            return null;
//        }
//
//        int program = GLES20.glCreateProgram();
//        if (program != 0) {
//            GLES20.glAttachShader(program, vertexShader);
//            if( GLES20.glGetError() != GLES20.GL_NO_ERROR )
//            	return null;
//            GLES20.glAttachShader(program, pixelShader);
//            if( GLES20.glGetError() != GLES20.GL_NO_ERROR )
//            	return null;
//            GLES20.glLinkProgram(program);
//            int[] linkStatus = new int[1];
//            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
//            if (linkStatus[0] != GLES20.GL_TRUE) {
//                Log.e("BaseProcedure", "Could not link program: ");
//                Log.e("BaseProcedure", GLES20.glGetProgramInfoLog(program));
//                GLES20.glDeleteProgram(program);
//                program = 0;
//            }
//        }
//        return new OpenGLHelper2(program,vertexShader,pixelShader);
//    }

    static public int createProgram(String vertexSource, String fragmentSource) {
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return -1;
        }
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return -2;
        }

        int program = GLES20.glCreateProgram();
        if (program != 0) {
            GLES20.glAttachShader(program, vertexShader);
            if( GLES20.glGetError() != GLES20.GL_NO_ERROR )
                return -3;
            GLES20.glAttachShader(program, pixelShader);
            if( GLES20.glGetError() != GLES20.GL_NO_ERROR )
                return -4;
            GLES20.glLinkProgram(program);
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE) {
                Log.e("BaseProcedure", "Could not link program: ");
                Log.e("BaseProcedure", GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = -5;
            }else{

            }
        }
        return program;
    }
	
	static public void deleteProgram( int[] p){

		if( p != null && p[0] != 0 ){
			GLES20.glDetachShader( p[0], p[1] );
			GLES20.glDeleteShader(p[1]);
			GLES20.glDetachShader( p[0], p[2] );
			GLES20.glDeleteShader(p[2]);
			GLES20.glDeleteProgram(p[0]);
		}
	}
	static public int loadShader(int shaderType, String source) {
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0) {
            GLES20.glShaderSource(shader, source);
            GLES20.glCompileShader(shader);
            int[] compiled = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {
                Log.e("BaseProcedure", "Could not compile shader " + shaderType + ":");
                Log.e("BaseProcedure", GLES20.glGetShaderInfoLog(shader));
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }
	
	static public int loadTextureFromDrawable(Context context, int drawableid){
		int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

        Bitmap bitmap;
        try {
        	bitmap = BitmapFactory.decodeResource(context.getResources(), drawableid);
        } finally {
        }

        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
        return textures[0];
	}
	static public int loadTextureFromBitmap( Bitmap bitmap){
		int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
        return textures[0];
	}
	
	static public int genTexture(){
		int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
        
        return textures[0];
	}
	static public String readScriptFromRawRes(Context context, int id){
        String result = "";
            try {   
                InputStream in = context.getResources().openRawResource(id);
                InputStreamReader inputreader = new InputStreamReader(in);
                BufferedReader buffreader = new BufferedReader(inputreader);
                String line;
                while (( line = buffreader.readLine()) != null) {
                    result += line + "\n";
                }                
            } catch (Exception e) {
                e.printStackTrace();   
            }   
            return result;   
    }

	static public int loadTextureFromFile( String name ){
        Bitmap bitmap;
        try {
            bitmap = BitmapFactory.decodeFile(name);
            int[] textures = new int[1];
            GLES20.glGenTextures(1, textures, 0);

            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);

            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
            bitmap.recycle();
            return textures[0];
        }catch(Exception e){
        }
        return -1;
	}
}
